package mvc;


import com.alibaba.fastjson2.JSON;
import frame.BeanPostProcessor;
import frame.Component;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;

@Component
public class DispatcherServlet extends HttpServlet implements BeanPostProcessor {

    private Map<String,WebHandler> handlerMap = new HashMap<>();

    private Map<String,Interceptor> interceptorMap = new HashMap<>();

    private static final Pattern pattern = Pattern.compile("yyq\\{(.*?)}");

    public DispatcherServlet(){
        interceptorMap.put("MyInterceptor",new MyInterceptor());
    }

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        WebHandler handler = findHandler(req);
        if(handler == null) {
            resp.setContentType("text/htmll;charset=UTF-8");
            resp.getWriter().println("Error! Your request has not mapping handler");
            return;
        }
        for (Interceptor interceptor : interceptorMap.values()) {
            if(!interceptor.preHandle(req,resp)){
                return;
            }
        }
        Object controllerBean = handler.getControllerBeam();
        Method method = handler.getMethod();
        try {
            Object[] args = resolveArgs(req,resp,method);
            Object result = method.invoke(controllerBean,args);
            switch (handler.getResultType()){
                case HTML -> {
                    resp.setContentType("text/html");
                    resp.getWriter().write(result.toString());
                }
                case JSON -> {
                    resp.setContentType("application/json;charset=UTF-8");
                    resp.getWriter().write(JSON.toJSONString(result));
                }
                case LOCAL -> {
                    ModelAndView mv = (ModelAndView) result;
                    String viewName = mv.getViewName();
                    InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(viewName);
                    try (resourceAsStream){
                        String html = new String(resourceAsStream.readAllBytes());
                        html = renderTemplate(html,mv.getContext());
                        resp.setContentType("text/html");
                        resp.getWriter().write(html);
                    }

                }
            }
            for (Interceptor interceptor : interceptorMap.values()) {
                interceptor.postHandle(req,resp);
            }
        } catch (IllegalAccessException | InvocationTargetException e) {
            throw new RuntimeException(e);
        }
    }

    private String renderTemplate(String html, Map<String, String> context) {
        // 创建一个 Matcher 对象，用于在 html 字符串中查找匹配的模板占位符
        java.util.regex.Matcher matcher = pattern.matcher(html);
        StringBuffer sb = new StringBuffer();
        // 循环查找匹配的占位符
        while (matcher.find()) {
            // 获取占位符中的 key
            String key = matcher.group(1);
            // 从 context 中获取对应 key 的值
            String value = context.getOrDefault(key, "");
            // 将匹配到的占位符替换为 context 中的值
            matcher.appendReplacement(sb, value);
        }
        // 将剩余的部分追加到结果中
        matcher.appendTail(sb);
        return sb.toString();
    }

    private Object[] resolveArgs(HttpServletRequest req, HttpServletResponse resp, Method method) {
        Object[] args = new Object[method.getParameterCount()];
        for (int i = 0; i < method.getParameterCount(); i++) {
            Parameter parameter = method.getParameters()[i];
            String value = null;
            if(parameter.isAnnotationPresent(Param.class)){
                value = req.getParameter(parameter.getAnnotation(Param.class).value());
            }else {
                value = req.getParameter(parameter.getName());
            }
            Class<?> type = parameter.getType();
            if(String.class.isAssignableFrom(type)){
                args[i] = value;
            }else if(Integer.class.isAssignableFrom(type)){
                args[i] = Integer.valueOf(value);
            }else {
                args[i] = null;
            }
        }
        return args;
    }

    private WebHandler findHandler(HttpServletRequest req) {
        return handlerMap.get(req.getRequestURI());
    }

    @Override
    public Object afterInitializeBean(Object bean, String beanName) {
       if(!bean.getClass().isAnnotationPresent(RequestMapping.class)){
           return bean;
       }
       RequestMapping classRm = bean.getClass().getAnnotation(RequestMapping.class);
       String classUrl = classRm.value();
        Arrays.stream(bean.getClass().getMethods())
                .filter(method -> method.isAnnotationPresent(RequestMapping.class))
                .forEach(method -> {
                    RequestMapping methodRm = method.getAnnotation(RequestMapping.class);
                    String key = classUrl.concat(methodRm.value());
                    WebHandler handler = new WebHandler(bean,method);
                    if(handlerMap.put(key,handler) != null){
                        throw new RuntimeException(beanName + " has duplicate mapping url:" + key);
                    }
                });
        return bean;

    }
}
